﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;

namespace NeuroSpeech.WebAtoms.Entity
{
    public class GlobalNamedLock
    {
        private Mutex mtx;

        public GlobalNamedLock(string strLockName)
        {
            //Name must be provided!
            if (string.IsNullOrWhiteSpace(strLockName))
            {
                //Use default name
                strLockName = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value.ToString();
            }

            //Create security permissions for everyone
            //It is needed in case the mutex is used by a process with
            //different set of privileges than the one that created it
            //Setting it will avoid access_denied errors.
            MutexSecurity mSec = new MutexSecurity();
            mSec.AddAccessRule(new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null),
                MutexRights.FullControl, AccessControlType.Allow));

            //Create the global mutex
            bool bCreatedNew;
            mtx = new Mutex(false, @"Global\" + strLockName, out bCreatedNew, mSec);
        }

        public bool Lock()
        {
            //Enter critical section
            //INFO: May throw an exception!
            //RETURN:
            //      = 'true' if successfully entered
            //      = 'false' if failed (DO NOT continue!)

            //Wait
            return mtx.WaitOne();
        }

        public void Unlock()
        {
            //Leave critical section
            //INFO: May throw an exception!

            //Release it
            mtx.ReleaseMutex();
        }
    }

    public static class Utils
    {
        #region Method Log
        private static object LockObject = new object();
        public static void Log(params Exception[] exs)
        {
            StringBuilder sb = new StringBuilder();
            foreach (Exception ex in exs)
                sb.AppendLine(ex.ToString());
            Log(sb.ToString());
        }
        #endregion


        public static void Log(string lines)
        {
            Log(lines, false);
        }

        public static void Log(string lines, bool logData)
        {
            var mut = new GlobalNamedLock(logsFolder.Replace("\\", "-"));

            if (!mut.Lock())
            {
                throw new Exception("Could not acquire Lock for log file...");
            }
            try
            {
                string logFile = GetLogsFolder();
                DateTime now = DateTime.Today;
                logFile += string.Format("{0}-{1:00}-{2:00}.txt", now.Year, now.Month, now.Day);
                StreamWriter sw = new StreamWriter(logFile, true);
                sw.WriteLine();
                HttpRequest request = HttpContext.Current.Request;
                sw.WriteLine("Url      :" + request.Url.ToString());
                sw.WriteLine("Method   :" + request.HttpMethod);
                sw.WriteLine("Agent    :" + request.UserAgent);
                sw.WriteLine("Host     :" + request.UserHostAddress);
                sw.WriteLine("Time     :" + DateTime.Now.ToString());
                if (HttpContext.Current.User != null)
                    sw.WriteLine("User     :" + HttpContext.Current.User.Identity.Name);
                if (request.UrlReferrer != null)
                    sw.WriteLine("Referral :" + request.UrlReferrer.ToString());
                if (logData)
                {
                    sw.WriteLine("Request: ");
                    foreach (string key in request.Form.Keys)
                    {
                        sw.WriteLine("\t" + key + " : " + request.Form[key]);
                    }
                    request.InputStream.Position = 0;
                    StreamReader sin = new StreamReader(request.InputStream);
                    sw.WriteLine(sin.ReadToEnd());
                    sin.Close();
                }
                /*foreach (Exception e in ex)
                {
                    sw.WriteLine();
                    sw.WriteLine(e.ToString());
                }*/

                // split in characters of max 100
                sw.WriteLine(lines);

                sw.WriteLine("-------------------------------------- END -----------------------------------");
                sw.Close();
            }
            finally
            {
                mut.Unlock();
            }
        }

        private static string logsFolder = null;

        private static string GetLogsFolder()
        {
            if (logsFolder == null)
            {
                logsFolder = HttpContext.Current.Server.MapPath("/logs/");
            }
            return logsFolder;
        }

        public static void Prepare()
        {
            GetLogsFolder();
        }

        #region internal static string QueryString<T1>(string p)
        internal static T QueryString<T>(string p, T def = default(T))
        {
            T value = def;
            string v = HttpContext.Current.Request.QueryString[p];
            if (v != null)
            {
                value = (T)Convert.ChangeType(v, typeof(T));
            }
            return value;
        }
        #endregion

        internal static List<long> SplitIDs(string ctIDs)
        {
            List<long> ids = new List<long>();
            foreach (string token in ctIDs.Split(',', ';'))
            {
                if (string.IsNullOrWhiteSpace(token))
                    continue;
                ids.Add(long.Parse(token));
            }
            return ids;
        }


        public static string Last(this string src, int count)
        {
            if (src.Length < count)
                return src;
            return src.Substring(src.Length - count);
        }


        public static string ToHtmlString(this string text, string def = null)
        {
            if (string.IsNullOrWhiteSpace(text))
                return def;
            return text.Replace("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;").Replace("\n", "<br/>").Replace("\t", "&nbsp;&nbsp;&nbsp;");
        }

        public static bool EqualsIgnoreCase(this string text, string other)
        {
            if (text == null)
                return other == null;
            if (other == null)
                return false;
            return string.Compare(text, other, true) == 0;
        }

        public static string ToSafeFileName(this string text)
        {
            if (string.IsNullOrWhiteSpace(text))
                return "-";
            return (new String(text.Select(x => Char.IsLetterOrDigit(x) ? x : '-').ToArray())).Replace("--", "-");
        }

        public static IEnumerable<IEnumerable<T>> Slice<T>(this IEnumerable<T> src, int block = 2)
        {
            List<List<T>> items = new List<List<T>>();
            while (src.Any())
            {
                items.Add(new List<T>(src.Take(block)));
                src = src.Skip(block);
            }
            return items;
        }

    }
}
